package git.soulbgm.utils;

import git.soulbgm.config.KeyConfig;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

/**
 * AES对称加密工具类
 *
 * @author SoulBGM
 * @date 2023-09-28
 */
public class AesUtil {

    private AesUtil() {
    }

    /**
     * 偏移量
     * <p>
     * 说明：偏移量字符串必须是16位 当模式是CBC的时候必须设置偏移量
     * 此处值与前端偏移量值保持一致
     */
    private static final IvParameterSpec IV_PARAMETER_SPEC = new IvParameterSpec(KeyConfig.getConfig().getIv().getBytes(StandardCharsets.UTF_8));

    /**
     * 加密算法
     */
    private static final String ALGORITHM = "AES";

    /**
     * 算法/模式/补码方式
     */
    private static final String ALGORITHM_PROVIDER = "AES/CBC/PKCS5Padding";

    /**
     * 加密
     *
     * @param src       加密内容
     * @param uniqueKey 加密key
     * @return {@link String}
     * @throws NoSuchPaddingException             无此类填充例外
     * @throws NoSuchAlgorithmException           没有这样的算法例外
     * @throws InvalidKeyException                无效算法参数异常
     * @throws BadPaddingException                无效密钥异常
     * @throws IllegalBlockSizeException          解码器异常
     * @throws InvalidAlgorithmParameterException 错误填充异常
     */
    public static String encrypt(String src, String uniqueKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException {
        byte[] key = uniqueKey.getBytes();
        SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM_PROVIDER);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, IV_PARAMETER_SPEC);
        byte[] cipherBytes = cipher.doFinal(src.getBytes(StandardCharsets.UTF_8));
        return byteToHexString(cipherBytes);
    }

    /**
     * 解密
     *
     * @param enc       加密内容
     * @param uniqueKey 唯一key
     * @return {@link String}
     * @throws NoSuchPaddingException             无此类填充例外
     * @throws NoSuchAlgorithmException           没有这样的算法例外
     * @throws InvalidAlgorithmParameterException 无效算法参数异常
     * @throws InvalidKeyException                无效密钥异常
     * @throws BadPaddingException                错误填充异常
     * @throws IllegalBlockSizeException          非法块大小异常
     */
    public static String decrypt(String enc, String uniqueKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
        byte[] key = uniqueKey.getBytes();
        SecretKey secretKey = new SecretKeySpec(key, ALGORITHM);
        Cipher cipher = Cipher.getInstance(ALGORITHM_PROVIDER);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, IV_PARAMETER_SPEC);
        byte[] hexBytes = hexStringToBytes(enc);
        byte[] plainBytes = cipher.doFinal(hexBytes);
        return new String(plainBytes, StandardCharsets.UTF_8);
    }

    /**
     * 将byte数组转换为16进制字符串
     *
     * @param data byte数组
     * @return {@link String}
     */
    private static String byteToHexString(byte[] data) {
        StringBuilder sb = new StringBuilder();
        for (byte d : data) {
            sb.append(String.format("%02x", d));
        }
        return sb.toString();
    }

    /**
     * 将16进制字符串转换为byte数组
     *
     * @param hexString 十六进制字符串
     * @return {@link byte[]}
     */
    private static byte[] hexStringToBytes(String hexString) {
        int ohl = 2;
        byte[] bytes = new byte[hexString.length() / ohl];
        for (int i = 0; i < hexString.length(); i += ohl) {
            String pair = hexString.substring(i, i + ohl);
            bytes[i / ohl] = (byte) ((Character.digit(pair.charAt(0), 16) << 4)
                    + Character.digit(pair.charAt(1), 16));
        }
        return bytes;
    }

}
